/*****************************************************************************
*   ME32g030_i2cm.c:  source file for i2c master module
*		ver 1.0
******************************************************************************/
#include "ME32g030_i2cm.h"
extern uint32_t SystemCoreClock;

/*****************************************************************************
Function Name	I2C_Init
Function Definition	void I2C_Init(I2C0_Type *i2c)
Function Description	Initialize I2C Interface
Input Parameters	*i2c : pointer to i2c, I2C0/I2C1
Return Value	none
Condition	No
Function called	-

*****************************************************************************/
void I2C_Init(I2C0_Type *i2c)
{
	/* Enable I2C clock */
  switch  ((uint32_t)i2c)
	{
		case (uint32_t)I2C0:
			SYSCON->SYSAHBCLKCTRL_b. I2C0_CLK=1;
			SYSCON->PRESETCTRL_b .I2C0_RST_N =0;
			SYSCON->PRESETCTRL_b .I2C0_RST_N=1;
		break;
		case (uint32_t)I2C1:
			SYSCON->SYSAHBCLKCTRL_b. I2C1_CLK=1;
			SYSCON->PRESETCTRL_b .I2C1_RST_N =0;
			SYSCON->PRESETCTRL_b .I2C1_RST_N=1;	
		break;

		default:
			return;
	}
	/* Reset I2C state machine */
	/* Reset STA, AA and SI. Stop flag should not be cleared as it is a reserved bit */
	i2c->CONCLR = I2C_CONCLR_AAC |I2C_CONCLR_SIC | I2C_CONCLR_STAC;
	i2c->CONSET = I2C_CONSET_AA | I2C_CONSET_I2CEN | I2C_CONSET_I2CINTEN;
}

/*****************************************************************************
Function Name	I2C_DeInit
Function Definition	void I2C_DeInit(I2C0_Type *i2c)
Function Description	Shutdown I2C Interface
Input Parameters	*i2c : pointer to i2c, I2C0/I2C1
Return Value	none
Condition	No
Function called	-

*****************************************************************************/
void I2C_DeInit(I2C0_Type *i2c)
{

	/* Reset I2C state machine */
	/* Reset STA, AA and SI. Stop flag should not be cleared as it is a reserved bit */
	i2c->CONCLR = I2C_CONCLR_AAC |I2C_CONCLR_SIC | I2C_CONCLR_STAC;
	/* Disable I2C clock */
  switch  ((uint32_t)i2c)
	{
		case (uint32_t)I2C0:
			SYSCON->SYSAHBCLKCTRL_b. I2C0_CLK=0;
		break;
		case (uint32_t)I2C1:
			SYSCON->SYSAHBCLKCTRL_b. I2C1_CLK=0;
		break;
		default:
			return;
	}
}

/*****************************************************************************
Function Name	I2C_DeInit
Function Definition	void I2C_DeInit(I2C0_Type *i2c)
Function Description	Set up bus speed for I2C controller
Input Parameters	*i2c : pointer to i2c, I2C0/I2C1
uint32_t busSpeed: i2c clock speed
Return Value	none
Condition	No
Function called	-

*****************************************************************************/
void I2C_Master_SetBusSpeed(I2C0_Type *i2c, uint32_t busSpeed)
{
	uint32_t n,m;
	n = SystemCoreClock/(SYSCON->SYSAHBCLKDIV *busSpeed*10);
	m=0;
	while(m<8)
	{
		if (n<17)
			break;
		n>>=1;m++;
	}
	if (m>=8)
	{
		i2c->CLK_b.N=0xF;
		i2c->CLK_b.M=0x7;		
	}else
	{
		i2c->CLK_b.N=n-1;
		i2c->CLK_b.M=m;
	}
	return;
}
/** Ignore NACK during data transfer. By default transfer is aborted. */
#define I2CM_XFER_OPTION_IGNORE_NACK     0x01
/** ACK last byte received. By default we NACK last byte we receive per I2C spec. */
#define I2CM_XFER_OPTION_LAST_RX_ACK     0x02



#define I2CM_STATUS_OK              0x00		/*!< Requested Request was executed successfully. */
#define I2CM_STATUS_ERROR           0x01		/*!< Unknown error condition. */
#define I2CM_STATUS_NAK             0x02		/*!< No acknowledgement received from slave. */
#define I2CM_STATUS_BUS_ERROR       0x03		/*!< I2C bus error */
#define I2CM_STATUS_SLAVE_NAK       0x04		/*!< No device responded for given slave address during SLA+W or SLA+R */
#define I2CM_STATUS_ARBLOST         0x05		/*!< Arbitration lost. */
#define I2CM_STATUS_BUSY            0xFF		/*!< I2C transmitter is busy. */

#define I2C_CON_AA             ((0x04))/*!< Assert acknowledge flag */
#define I2C_CON_SI             ((0x08))/*!< I2C interrupt flag */
#define I2C_CON_STO            ((0x10))/*!< STOP flag */
#define I2C_CON_STA            ((0x20))/*!< START flag */

/* Control flags */
#define I2C_CON_FLAGS (I2C_CON_AA | I2C_CON_SI | I2C_CON_STO | I2C_CON_STA)
/*****************************************************************************
Function Name	 I2C_Master_XferHandler
Function Definition	uint32_t I2C_Master_XferHandler(I2C0_Type *i2c, I2C_XFER_Type *xfer)
Function Description	Transfer state change handler
Input Parameters	*i2c : pointer to i2c, I2C0/I2C1
xfer	: Pointer to a I2CM_XFER_T structure see notes below
Return Value	Returns non-zero value on completion of transfer. The @a status
          member of @a xfer structure contains the current status of the
					transfer at the end of the call.
Condition	No
Function called	-

*****************************************************************************/
uint32_t I2C_Master_XferHandler(I2C0_Type *i2c, I2C_XFER_Type *xfer)
{
	uint32_t cclr = I2C_CON_FLAGS;

	switch (I2C_GetCurState(i2c)) {
	case I2C_STAT_M_TX_START:		/* Start condition on bus */
	case I2C_STAT_M_TX_RESTART:		/* Repeated start condition */
		i2c->DAT = (xfer->slaveAddr << 1) | (xfer->txSz == 0);
		break;

	/* Tx handling */
	case I2C_STAT_M_TX_SLAW_NACK:		/* SLA+W sent NAK received */
	case I2C_STAT_M_TX_DAT_NACK:		/* DATA sent NAK received */
		if ((xfer->options & I2CM_XFER_OPTION_IGNORE_NACK) == 0) {
			xfer->status = I2CM_STATUS_NAK;
			cclr &= ~I2C_CON_STO;
			break;
		}

	case I2C_STAT_M_TX_SLAW_ACK:		/* SLA+W sent and ACK received */
	case I2C_STAT_M_TX_DAT_ACK:		/* DATA sent and ACK received */
		if (!xfer->txSz) {
			if (xfer->rxSz) {
				cclr &= ~I2C_CON_STA;
			}
			else {
				xfer->status = I2CM_STATUS_OK;
				cclr &= ~I2C_CON_STO;
			}

		}
		else {
			i2c->DAT = *xfer->txBuff++;
			xfer->txSz--;
		}
		break;

	/* Rx handling */
	case I2C_STAT_M_RX_DAT_NACK:		/* Data Received and NACK sent */
	case I2C_STAT_M_RX_DAT_ACK:		/* Data Received and ACK sent */
		*xfer->rxBuff++ = i2c->DAT;
		xfer->rxSz--;

	case I2C_STAT_M_RX_SLAR_ACK:		/* SLA+R sent and ACK received */
		if ((xfer->rxSz > 1) || (xfer->options & I2CM_XFER_OPTION_LAST_RX_ACK)) {
			cclr &= ~I2C_CON_AA;
		}
		if (xfer->rxSz == 0) {
			xfer->status = I2CM_STATUS_OK;
			cclr &= ~I2C_CON_STO;
		}
		break;

	/* NAK Handling */
	case I2C_STAT_M_RX_SLAR_NACK:		/* SLA+R sent NAK received */
		xfer->status = I2CM_STATUS_SLAVE_NAK;
		cclr &= ~I2C_CON_STO;
		break;

	case I2C_STAT_M_TX_ARB_LOST:		/* Arbitration lost */
		xfer->status = I2CM_STATUS_ARBLOST;
		break;

	case I2C_BUS_ERROR:		/* Bus Error */
		xfer->status = I2CM_STATUS_BUS_ERROR;
		cclr &= ~I2C_CON_STO;
        break;
      
    default:
		xfer->status = I2CM_STATUS_ERROR;
		cclr &= ~I2C_CON_STO;
        break;
	}

	/* Set clear control flags */
	i2c->CONSET = cclr ^ I2C_CON_FLAGS;
	/* Stop flag should not be cleared as it is a reserved bit */
	i2c->CONCLR = cclr & (I2C_CON_AA | I2C_CON_SI | I2C_CON_STA);

	return xfer->status != I2CM_STATUS_BUSY;
}
/*****************************************************************************
Function Name	I2C_Set_Slave_Addr
Function Definition	void I2C_Set_Slave_Addr(I2C0_Type *i2c, I2C_SLAVE_ID sid, uint8_t addr, uint8_t mask)
Function Description	Set up I2C slave address
Input Parameters	*i2c : pointer to i2c, I2C0/I2C1
uint32_t busSpeed: i2c slave address and address mask
Return Value	none
Condition	No
Function called	-

*****************************************************************************/
void I2C_Set_Slave_Addr(I2C0_Type *i2c, I2C_SLAVE_ID sid, uint8_t addr, uint8_t mask)
{
	switch(sid)
	{
		case I2C_SLAVE_0:
			i2c->ADR0 = addr;
			i2c->ADM0 = mask;
			break;
		case I2C_SLAVE_1:
			i2c->ADR1 = addr;
			i2c->ADM1 = mask;
			break;
		case I2C_SLAVE_2:
			i2c->ADR2 = addr;
			i2c->ADM2 = mask;
			break;
		case I2C_SLAVE_3:
			i2c->ADR3 = addr;
			i2c->ADM3 = mask;
			break;
		case I2C_SLAVE_GENERAL:
			break;
		default:
			break;
	}
}
/*****************************************************************************
Function Name	 I2C_Slave_XferHandler
Function Definition	uint32_t I2C_Slave_XferHandler(I2C0_Type *i2c, I2C_XFER_Type *xfer)
Function Description	Transfer state change handler
Input Parameters	*i2c : pointer to i2c, I2C0/I2C1
xfer	: Pointer to a I2C_XFER_Type structure see notes below
Return Value	Returns non-zero value on completion of transfer. The @a status
          member of @a xfer structure contains the current status of the
					transfer at the end of the call.
Condition	No
Function called	-

*****************************************************************************/
uint32_t I2C_Slave_XferHandler(I2C0_Type *i2c, I2C_XFER_Type *xfer)
{
	uint32_t cclr = I2C_CON_FLAGS;
	int ret = RET_SLAVE_BUSY;

	xfer->status = I2C_STATUS_BUSY;
	switch (I2C_GetCurState(i2c)) 
	{
		case I2C_STAT_S_RX_PRE_SLA_DAT_ACK:		/* SLA: Data received + ACK sent */
		case I2C_STAT_S_RX_PRE_GENCALL_DAT_ACK:		/* GC: Data received + ACK sent */
			*xfer->rxBuff++ = i2c->DAT;
		
//			xfer->rxSz--;
			ret = RET_SLAVE_RX;
//			if (xfer->rxSz > 1) {
				cclr &= ~I2C_CON_AA;
//			}
			break;

		case I2C_STAT_S_RX_SLAW_ACK:		/* Own SLA+W received */
		case I2C_STAT_S_RX_ARB_LOST_M_SLA:		/* Own SLA+W received after losing arbitration */
		case I2C_STAT_S_RX_GENCALL_ACK:		/* GC+W received */
		case I2C_STAT_S_RX_ARB_LOST_M_GENCALL:		/* GC+W received after losing arbitration */
			xfer->slaveAddr = i2c->DAT & ~1;
//			if (xfer->rxSz > 1) {
				cclr &= ~I2C_CON_AA;
//			}
			break;

		case I2C_STAT_S_TX_SLAR_ACK:		/* SLA+R received */
		case I2C_STAT_S_TX_ARB_LOST_M_SLA:		/* SLA+R received after losing arbitration */
			xfer->slaveAddr = i2c->DAT & ~1;

		case I2C_STAT_S_TX_DAT_ACK:		/* DATA sent and ACK received */
			i2c->DAT = *xfer->txBuff++;
//			xfer->txSz--;
//			if (xfer->txSz > 0) {
				cclr &= ~I2C_CON_AA;
//			}
			ret = RET_SLAVE_TX;
			break;

		case I2C_STAT_S_TX_DAT_NACK:		/* Data transmitted and NAK received */
		case I2C_STAT_S_TX_LAST_DAT_ACK:		/* Last data transmitted and ACK received */
		case I2C_STAT_S_RX_PRE_SLA_DAT_NACK:		/* SLA: Data received + NAK sent */
		case I2C_STAT_S_RX_PRE_GENCALL_DAT_NACK:		/* GC: Data received + NAK sent */
		case I2C_STAT_S_RX_STA_STO_SLVREC_SLVTRX:		/* STOP/Repeated START condition received */
			ret = RET_SLAVE_IDLE;
			cclr &= ~I2C_CON_AA;
			xfer->status = I2C_STATUS_DONE;
			if (xfer->slaveAddr & 1) {
				cclr &= ~I2C_CON_STA;
			}
			break;
	}

	/* Set clear control flags */
	i2c->CONSET = cclr ^ I2C_CON_FLAGS;
	i2c->CONCLR = cclr & ~I2C_CON_STO;

	return ret;	
}